using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Collections;
using NovodexWrapper;

namespace marbletrack
{
    class Debug
    {
        BasicEffect wireEffect;
        BasicEffect texturedEffect;
        BasicEffect axisEffect;

        public Matrix Position = Matrix.Identity;

        KeyboardState previousKeyboardState;

        public bool WireFrameFlag;
        public bool TexturedMeshFlag;

        VertexDeclaration vertexDeclaration;

        //Setup the basic effects used to draw wireframe mesh, triangle mesh and axis
        public Debug(Matrix viewMatrix, Matrix projectionMatrix)
        {
            wireEffect = new BasicEffect(Game1.Graphics.GraphicsDevice, null);
            wireEffect.DiffuseColor = Color.Blue.ToVector3();

            texturedEffect = new BasicEffect(Game1.Graphics.GraphicsDevice, null);
            texturedEffect.EnableDefaultLighting();
            texturedEffect.TextureEnabled = true;
            texturedEffect.Texture = Game1.contentloader.Load<Texture2D>("test");

            axisEffect = new BasicEffect(Game1.Graphics.GraphicsDevice, null);

            WireFrameFlag = false;
            TexturedMeshFlag = false;

            previousKeyboardState = Keyboard.GetState();

            //Vertex Declaration
            vertexDeclaration = new VertexDeclaration(Game1.Graphics.GraphicsDevice, VertexPositionNormalTexture.VertexElements);
        }

        //This is used for button activation of debug code
        public void Update()
        {
            KeyboardState keyboardState = Keyboard.GetState();

            if (keyboardState.IsKeyDown(Keys.D2) && previousKeyboardState.IsKeyUp(Keys.D2))
                WireFrameFlag = !WireFrameFlag;
            if (keyboardState.IsKeyDown(Keys.D3) && previousKeyboardState.IsKeyUp(Keys.D3))
                TexturedMeshFlag = !TexturedMeshFlag;

            previousKeyboardState = keyboardState;
        }

        public void Draw(ModelClass model, Matrix viewMatrix, Matrix projectionMatrix)
        {
            DrawWireFrame(model, viewMatrix, projectionMatrix);
            DrawTriangles(model, viewMatrix, projectionMatrix);
            DrawAxis(viewMatrix, projectionMatrix);
            Position = Matrix.Identity;
        }

        //Display axis on the screen
        public void DrawAxis(Matrix viewMatrix, Matrix projectionMatrix)
        {
            if (TexturedMeshFlag || WireFrameFlag)
            {
                Game1.Graphics.GraphicsDevice.VertexDeclaration = vertexDeclaration;

                VertexPositionNormalTexture[] vertexBuffer = new VertexPositionNormalTexture[] {
                    new VertexPositionNormalTexture(new Vector3(0, 200, 0), Vector3.Forward, Vector2.One),
                    new VertexPositionNormalTexture(new Vector3(400, 200, 0), Vector3.Forward, Vector2.One),
                    new VertexPositionNormalTexture(new Vector3(0, 600, 0), Vector3.Forward, Vector2.One),
                    new VertexPositionNormalTexture(new Vector3(0, 200, 400), Vector3.Forward, Vector2.One) };

                int[] xIndexBuffer = new int[] { 0, 1 };
                int[] yIndexBuffer = new int[] { 0, 2 };
                int[] zIndexBuffer = new int[] { 0, 3 };

                axisEffect.World = Matrix.Identity;
                axisEffect.View = viewMatrix;
                axisEffect.Projection = projectionMatrix;

                axisEffect.Begin();

                axisEffect.DiffuseColor = Color.Red.ToVector3();
                foreach (EffectPass pass in axisEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();

                    Game1.Graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.LineList, vertexBuffer, 0, vertexBuffer.Length, xIndexBuffer, 0, xIndexBuffer.Length / 2);

                    pass.End();
                }

                axisEffect.DiffuseColor = Color.Green.ToVector3();
                foreach (EffectPass pass in axisEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();

                    Game1.Graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.LineList, vertexBuffer, 0, vertexBuffer.Length, yIndexBuffer, 0, yIndexBuffer.Length / 2);

                    pass.End();
                }

                axisEffect.DiffuseColor = Color.Blue.ToVector3();
                foreach (EffectPass pass in axisEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();

                    Game1.Graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.LineList, vertexBuffer, 0, vertexBuffer.Length, zIndexBuffer, 0, zIndexBuffer.Length / 2);

                    pass.End();
                }

                axisEffect.End();
            }
        }

        //Draw a wireframe mesh of a model
        private void DrawWireFrame(ModelClass model, Matrix viewMatrix, Matrix projectionMatrix)
        {
            if (WireFrameFlag)
            {
                Game1.Graphics.GraphicsDevice.VertexDeclaration = vertexDeclaration;

                VertexPositionNormalTexture[] vertexBuffer = model.MeshVertexBuffers[0];
                int[] indexBuffer = model.MeshIndexBuffers[0];

                //Recreate index buffer so it can be used with the LineList primitive type as opposed to the TriangleList type
                int[] wireIndexBuffer = new int[2 * indexBuffer.Length];
                for (int i = 0, j = 0; i < indexBuffer.Length && j < wireIndexBuffer.Length; i += 3, j++)
                {
                    wireIndexBuffer[j] = indexBuffer[i];
                    wireIndexBuffer[++j] = indexBuffer[i + 1];
                    wireIndexBuffer[++j] = indexBuffer[i + 1];
                    wireIndexBuffer[++j] = indexBuffer[i + 2];
                    wireIndexBuffer[++j] = indexBuffer[i + 2];
                    wireIndexBuffer[++j] = indexBuffer[i];
                }

                wireEffect.World = Position; //* Matrix.CreateTranslation(Game1.HALFSQUARESIZE, 0, Game1.HALFSQUARESIZE);
                wireEffect.View = viewMatrix;
                wireEffect.Projection = projectionMatrix;

                wireEffect.Begin();

                foreach (EffectPass pass in wireEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();

                    Game1.Graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.LineList, vertexBuffer, 0, vertexBuffer.Length, wireIndexBuffer, 0, wireIndexBuffer.Length / 2);

                    pass.End();
                }

                wireEffect.End();
            }
        }

        // Reverses Cull Mode
        private void ReverseCullMode()
        {
            if (Game1.Graphics.GraphicsDevice.RenderState.CullMode == CullMode.CullClockwiseFace)
                Game1.Graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
            else if (Game1.Graphics.GraphicsDevice.RenderState.CullMode == CullMode.CullCounterClockwiseFace)
                Game1.Graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;
            else
                Game1.Graphics.GraphicsDevice.RenderState.CullMode = CullMode.None;
        }
        
        //Draw the triangle mesh of a model
        private void DrawTriangles(ModelClass model, Matrix viewMatrix, Matrix projectionMatrix)
        {
            if (TexturedMeshFlag)
            {
                Game1.Graphics.GraphicsDevice.VertexDeclaration = vertexDeclaration;

                VertexPositionNormalTexture[] vertexBuffer = model.MeshVertexBuffers[0];
                int[] indexBuffer = model.MeshIndexBuffers[0];

                texturedEffect.World = Position; //* Matrix.CreateTranslation(Game1.HALFSQUARESIZE, 0, Game1.HALFSQUARESIZE);
                texturedEffect.View = viewMatrix;
                texturedEffect.Projection = projectionMatrix;

                // Check wheither cullmode need to be reversed (Note: XNA draws triangles in the oposite order to the physx engine)
                if (!model.FlipNormalsFlag)
                    ReverseCullMode();

                texturedEffect.Begin();

                foreach (EffectPass pass in texturedEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();

                    Game1.Graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertexBuffer, 0, vertexBuffer.Length, indexBuffer, 0, indexBuffer.Length / 3);

                    pass.End();
                }

                texturedEffect.End();

                // Checks wheither cullmode needs to be switched back to default
                if (!model.FlipNormalsFlag)
                    ReverseCullMode();
            }
        }

        //Draw Orientated Bounding Box
        static public void DrawWorldBox(NxBox worldBox)
        {
            Game1.Graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(Game1.Graphics.GraphicsDevice, VertexPositionNormalTexture.VertexElements);

            VertexPositionNormalTexture[] vertexBuffer = new VertexPositionNormalTexture[worldBox.getPoints().Length];
            int[] indexBuffer = new int[worldBox.getTriangles().Length];

            for (int i = 0; i < indexBuffer.Length; i++)
            {
                indexBuffer[i] = (int)worldBox.getTriangles()[i];
            }

            for (int i = 0; i < vertexBuffer.Length; i++)
            {
                vertexBuffer[i].Position = MathConversion.ToVector3(worldBox.getPoints()[i]);
            }

            Game1.basicEffect.World = Matrix.Identity;
            Game1.basicEffect.View = Game1.view;
            Game1.basicEffect.Projection = Game1.projectionMatrix;

            Game1.basicEffect.Begin();

            foreach (EffectPass pass in Game1.basicEffect.CurrentTechnique.Passes)
            {
                pass.Begin();

                Game1.Graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(
                PrimitiveType.TriangleList, vertexBuffer, 0, vertexBuffer.Length, indexBuffer, 0, indexBuffer.Length / 3);

                pass.End();
            }
            Game1.basicEffect.End();
        }

        //Draw Axis Aligned Bounding Box
        static public void DrawWorldBounds(NxBounds3 worldBounds)
        {
            Game1.Graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(Game1.Graphics.GraphicsDevice, VertexPositionNormalTexture.VertexElements);

            VertexPositionNormalTexture[] vertexBuffer = new VertexPositionNormalTexture[8];
            int[] indexBuffer = new int[36] { 0, 1, 2, 0, 2, 3, 3, 2, 6, 3, 6, 7, 1, 5, 2, 2, 5, 6, 0, 5, 1, 0, 4, 5, 4, 6, 5, 4, 7, 6, 0, 3, 4, 3, 7, 4 }; ;

            float width = MathConversion.ToVector3(worldBounds.getExtents()).X;
            float height = MathConversion.ToVector3(worldBounds.getExtents()).Y;
            float depth = MathConversion.ToVector3(worldBounds.getExtents()).Z;
            Vector3 center = MathConversion.ToVector3(worldBounds.getCenter());

            vertexBuffer[0].Position = center + new Vector3(-width, -height, depth);
            vertexBuffer[1].Position = center + new Vector3(-width, height, depth);
            vertexBuffer[2].Position = center + new Vector3(width, height, depth);
            vertexBuffer[3].Position = center + new Vector3(width, -height, depth);
            vertexBuffer[4].Position = center + new Vector3(-width, -height, -depth);
            vertexBuffer[5].Position = center + new Vector3(-width, height, -depth);
            vertexBuffer[6].Position = center + new Vector3(width, height, -depth);
            vertexBuffer[7].Position = center + new Vector3(width, -height, -depth);

            Game1.basicEffect.World = Matrix.Identity;
            Game1.basicEffect.View = Game1.view;
            Game1.basicEffect.Projection = Game1.projectionMatrix;

            Game1.basicEffect.Begin();

            foreach (EffectPass pass in Game1.basicEffect.CurrentTechnique.Passes)
            {
                pass.Begin();
                
                Game1.Graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(
                PrimitiveType.TriangleList, vertexBuffer, 0, vertexBuffer.Length, indexBuffer, 0, indexBuffer.Length / 3);

                pass.End();
            }
            Game1.basicEffect.End();
        }
    }
}
